/*
 * LinkedList.cpp
 *
 *  Created on: Oct 1, 2010
 *      Author: Jacob
 */
#include "stdafx.h"
#include "LinkedList.h"

LinkedList* LinkedList::newLinkedList(bool circularlyLinked)
{
	return new LinkedList(circularlyLinked);
}

LinkedList::LinkedList(bool circularlyLinked)
{
	head = new Node();
	tail = new Node();
	this->head->setData("Starting Head");
	this->tail->setData("Starting Tail");
	head->setNext(this->tail);
	current = head;
	size = 2;
	dummyTailRemoved = false;
	dummyHeadRemoved = false;
	this->circularlyLinked = circularlyLinked;

	if(circularlyLinked)
		tail->setNext(head);
}

LinkedList::~LinkedList()
{
	this->tail->setNext(NULL);
	delete head;
}

void LinkedList::addToHead(string placeholder)
{
	Node* newNode = constructNodeFromData(placeholder);
	addNodeToHead(newNode);
}

void LinkedList::addToHead(Node* nodeToBeAdded)
{
	addNodeToHead(nodeToBeAdded);
}

void LinkedList::addNodeToHead(Node* nodeToBeAdded)
{
	if (overrideNullHead(nodeToBeAdded))
		return;

	nodeToBeAdded->setNext(head);
	head = nodeToBeAdded;
	modifySize(1);
}

bool LinkedList::overrideNullHead(Node* nodeToBeAdded)
{
	/*
	 * List is initialized with dummy head and tail
	 * When addtohead/tail is called for first time
	 * these are overwritten
	 */

	if (dummyHeadRemoved)
		return false;

	if (head->getData() == "Starting Head")
	{
		replaceNullHead(nodeToBeAdded);
		return true;
	}

	return false;
}

void LinkedList::replaceNullHead(Node* newHead)
{
	Node* nullHead = head;
	newHead->setNext(nullHead->getNext());
	head = newHead;
	modifySize(1);
	deleteNode(nullHead);
	dummyHeadRemoved = true;
}

void LinkedList::addToTail(string placeholder)
{
	Node* newNode = constructNodeFromData(placeholder);
	addNodeToTail(newNode);
}

void LinkedList::addToTail(Node* nodeToBeAdded)
{
	addNodeToTail(nodeToBeAdded);
}

void LinkedList::addNodeToTail(Node* nodeToBeAdded)
{
	if (overrideNullTail(nodeToBeAdded))
		return;

	tail->setNext(nodeToBeAdded);
	tail = nodeToBeAdded;
	makeListCircularlyLinked();
	modifySize(1);
}

bool LinkedList::overrideNullTail(Node* nodeToBeAdded)
{
	/*
	 * List is initialized with dummy head and tail
	 * When addtohead/tail is called for first time
	 * these are overwritten
	 */

	if (dummyTailRemoved)
		return false;

	setCurrentToHead();
	for (int index = 0; index < size; index++)
	{
		if (current->getNext() == tail && tail->getData() == "Starting Tail")
		{
			replaceNullTail(nodeToBeAdded);
			return true;
		}
		++(*this);
	}

	return false;
}

void LinkedList::replaceNullTail(Node* newTail)
{
	Node* nullTail = tail;
	current->setNext(newTail);
	tail = newTail;
	modifySize(1);
	deleteNode(nullTail);
	makeListCircularlyLinked();
	dummyTailRemoved = true;
}

void LinkedList::insert(string placeholder, int position)
{
	Node* nodeToBeInserted = constructNodeFromData(placeholder);
	this->insert(nodeToBeInserted, position);
}

void LinkedList::insert(Node* nodeToBeInserted, int position)
{
	findPositionAndInsertNode(nodeToBeInserted, position);
}

void LinkedList::findPositionAndInsertNode(Node* nodeToBeInserted, int position)
{
	setCurrentToHead();
	for (int index = 0; index < size; index++)
	{
		if (index == position)
		{
			insertNode(nodeToBeInserted);
			return;
		}
		++(*this);
	}
}

void LinkedList::insertNode(Node* nodeToBeInserted)
{
	if (current == tail)
	{
		addToTail(nodeToBeInserted);
	}
	else
	{
		nodeToBeInserted->setNext(current->getNext());
		current->setNext(nodeToBeInserted);
		modifySize(1);
	}
}

void LinkedList::removeFromHead()
{
	Node* oldHeadToBeDeleted = head;
	head = head->getNext();
	deleteNode(oldHeadToBeDeleted);
}

void LinkedList::removeFromTail()
{
	Node* oldTailToBeDeleted = tail;
	setCurrentToHead();
	for (int index = 0; index < size; index++)
	{
		if (current->getNext() == tail)
		{
			tail = current;
			tail->setNext(NULL);
			makeListCircularlyLinked();
			deleteNode(oldTailToBeDeleted);
		}
		++(*this);
	}
}

void LinkedList::remove(int position)
{
	if (removeFromHeadOrTail(position))
		return;

	Node* nodeToBeRemoved = getNodeAtPosition(position);

	if (nodeToBeRemoved != NULL)
	{
		prepareNodeForDeletion(nodeToBeRemoved);
	}
}

void LinkedList::remove(Node* nodeToBeRemoved)
{
	if(removeFromHeadOrTail(nodeToBeRemoved))
			return;

	prepareNodeForDeletion(nodeToBeRemoved);
}

void LinkedList::prepareNodeForDeletion(Node* nodeToBeDeleted)
{
	setCurrentToHead();
	for (int index = 0; index < size; index++)
	{
		if(current->getNext() == nodeToBeDeleted)
		{
			current->setNext(nodeToBeDeleted->getNext());
			deleteNode(nodeToBeDeleted);
		}

		++(*this);
	}
}

bool LinkedList::removeFromHeadOrTail(Node* nodeToBeRemoved)
{
	if (nodeToBeRemoved == head)
	{
		removeFromHead();
		return true;
	}
	else if (nodeToBeRemoved == tail)
	{
		removeFromTail();
		return true;
	}

	return false;
}

bool LinkedList::removeFromHeadOrTail(int position)
{
	int headPosition = 0;

	if (position == headPosition)
	{
		removeFromHead();
		return true;
	}
	else if (position == size - 1)
	{
		removeFromTail();
		return true;
	}

	return false;
}

Node* LinkedList::getNodeAtPosition(int position)
{
	setCurrentToHead();
	for (int index = 0; index < size; index++)
	{
		if (index == position)
		{
			return current;
		}
		++(*this);
	}

	return NULL;
}

Node* LinkedList::search(string value)
{
	setCurrentToHead();
	for (int index = 0; index < size; index++)
	{
		if (current->getData() == value)
		{
			return current;
		}
		++(*this);
	}

	return NULL;
}

Node* LinkedList::search(Node* nodeToBeFound)
{
	setCurrentToHead();
	for (int index = 0; index < size; index++)
	{
		if (current == nodeToBeFound)
		{
			return current;
		}
		++(*this);
	}

	return NULL;
}

Node* LinkedList::getCurrent()
{
	return this->current;
}

void LinkedList::setCurrent(Node* current)
{
	this->current = current;
}

int LinkedList::getSize()
{
	return this->size;
}

string LinkedList::toString()
{
	ostringstream stringStream;

	setCurrentToHead();
	for (int index = 0; index < size; index++)
	{
		stringStream << "\n" << current->getData() << "    PTR: " << current;
		++(*this);
	}

	stringStream << "\n\n" << "Linked List Size: " << size;
	stringStream << "\n" << "Head: " << head->getData() << "   PTR: " << head;
	stringStream << "\n" << "Tail: " << tail->getData() << "   PTR: " << tail;

	return stringStream.str();
}

void LinkedList::setCurrentToHead()
{
	current = head;
}

void LinkedList::deleteNode(Node* nodeToBeDeleted)
{
	nodeToBeDeleted->setNext(NULL);
	delete nodeToBeDeleted;
	modifySize(-1);
}

void LinkedList::modifySize(int value)
{
	size += value;
}

void LinkedList::makeListCircularlyLinked()
{
	if(circularlyLinked)
		this->tail->setNext(head);
}

void LinkedList::iterate()
{
	current = current->getNext();
}

LinkedList& LinkedList::operator++()
{
	current = current->getNext();
	return *this;
}

Node* LinkedList::constructNodeFromData(string placeholder)
{
	Node* newNode = Node::newNodeFromData(placeholder);
	return newNode;
}
